﻿using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(CubicBezierSpline))]
public class BezierPathEditor : Editor {

	CubicBezierSpline bezierPath;
	private const float handleLength = 0.25f;

	bool shouldDisplayPoints = false;

	void OnEnable(){
		bezierPath = (CubicBezierSpline) target;

	}

	public override void OnInspectorGUI () {
		serializedObject.Update();
		RenderCustomEditorMenu();
		serializedObject.ApplyModifiedProperties();
	}

	void RenderCustomEditorMenu(){
		EditorGUILayout.LabelField("Cubic Bezier Path: " + bezierPath.name, EditorStyles.boldLabel);
		EditorGUILayout.LabelField("Number of curves : " + bezierPath.curveCount.ToString("00"));

		if(bezierPath.controlPoints != null){
			GUILayout.BeginHorizontal();
			if(GUILayout.Button("Add Curve")){
				AddCurve();
			}
			if(GUILayout.Button("Remove Curve")){
				RemoveCurve();
				return;
			}
			GUILayout.EndHorizontal();

			shouldDisplayPoints = GUILayout.Toggle(shouldDisplayPoints, "Display curve points?");
			if(shouldDisplayPoints){
				DisplayPoints();
			}
		}
		else{
			if(GUILayout.Button("Create Quadratic Bezier Spline")){
				CreatePath();
			}
		}
	}

	void DisplayPoints(){
		for(int i = 0; i < bezierPath.controlPoints.Count; i++){
			EditorGUI.BeginChangeCheck();
			Vector3 newPoint = bezierPath.controlPoints[i];
			newPoint = EditorGUILayout.Vector3Field("Point " + i.ToString("00"), 
				newPoint);
			if(EditorGUI.EndChangeCheck()){
				Undo.RecordObject(target, "Changed Point " + i.ToString("00"));
				bezierPath.controlPoints[i] = newPoint;
				EditorUtility.SetDirty(target);
			}
		}
	}




	void OnSceneGUI(){

		serializedObject.Update();

		if(bezierPath.controlPoints != null){
			DrawHandlesOnSceneview();
			DrawCurveOnEditor();
		}
		serializedObject.ApplyModifiedProperties();
	}

	void DrawHandlesOnSceneview(){
		for(int i = 0; i < bezierPath.controlPoints.Count; i++){
			EditorGUI.BeginChangeCheck();
			Vector3 bezierPointInWorldSpace = bezierPath.transform.TransformPoint(bezierPath.controlPoints[i]);
	

			float sizeFactor = HandleUtility.GetHandleSize(bezierPointInWorldSpace);
			DrawHandle(ref bezierPointInWorldSpace, sizeFactor);
			DrawCrosshair(bezierPointInWorldSpace, sizeFactor);
			DrawHandleConectors(i);

			
			if(EditorGUI.EndChangeCheck()) {
				Undo.RecordObject(target, "Changed Point " + i.ToString("00"));
				bezierPath.controlPoints[i] = bezierPath.transform.InverseTransformPoint(bezierPointInWorldSpace);
				EditorUtility.SetDirty(target);
			}
		}
	}

	public void DrawHandle(ref Vector3 newPoint, float relativeScale = 1.0f){
		newPoint = Handles.FreeMoveHandle(newPoint, Quaternion.identity,
		                                  handleLength * 0.5f  * relativeScale, Vector3.zero, Handles.RectangleCap);
	}
	public void DrawCrosshair(Vector3 point, float relativeScale = 1.0f){
		Handles.DrawLine(point - Vector3.up * handleLength * relativeScale, point + Vector3.up * handleLength * relativeScale);
		Handles.DrawLine(point - Vector3.right * handleLength * relativeScale, point + Vector3.right * handleLength * relativeScale);
		Handles.DrawLine(point - Vector3.forward * handleLength * relativeScale, point + Vector3.forward * handleLength * relativeScale);
	}

	void DrawHandleConectors(int i){
		Handles.color = new Color(.7f, .7f, .7f);
		if(i % 3 == 0){
			if(i + 1 < bezierPath.controlPoints.Count){
				Handles.DrawLine(bezierPath.transform.TransformPoint(bezierPath.controlPoints[i]),
				                 bezierPath.transform.TransformPoint(bezierPath.controlPoints[i+1]));
			}
			if(i - 1 > 0){
					Handles.DrawLine(bezierPath.transform.TransformPoint(bezierPath.controlPoints[i]),
				                 bezierPath.transform.TransformPoint(bezierPath.controlPoints[i-1]));
			}
		}
	}

	void DrawCurveOnEditor(){
		CurveData curveData = GetCurveData();
		Handles.color = Color.green;
		var drawingPoints = bezierPath.GetDrawingPoints();
		for(int i = 0; i < drawingPoints.Count-1; i++){
			Handles.color = SetCurvePieceColor(curveData.minSpacing, curveData.maxSpacing, curveData.maxSpacing, curveData.distances[i]);
			Handles.DrawLine(drawingPoints[i], drawingPoints[i+1]);
		}
	}

	Color SetCurvePieceColor(float min, float max, float average, float current){
		float difference;
		float valueToCompare = (current > average ? max : min);
		difference = Mathf.Abs(current - valueToCompare);

		difference =  difference / Mathf.Abs(average - valueToCompare) ;

		return new Color (difference, 1 - difference, 0);


	}

	void CreatePath(){
		Undo.RecordObject(target, "Created a Path");
		bezierPath.controlPoints = new System.Collections.Generic.List<Vector3>();
		AddCurve();
		EditorUtility.SetDirty(target);
	}

	void AddCurve(){
		Undo.RecordObject(target, "Added a curve");
		if(bezierPath.controlPoints.Count == 0){
			bezierPath.controlPoints.Add(Vector3.zero);
		}
		Vector3 lastPoint = bezierPath.controlPoints[bezierPath.controlPoints.Count - 1];

		bezierPath.controlPoints.Add(lastPoint + Vector3.right);
		bezierPath.controlPoints.Add(lastPoint + Vector3.right + Vector3.up);
		bezierPath.controlPoints.Add(lastPoint + Vector3.right * 2 + Vector3.up);
		EditorUtility.SetDirty(target);
	}
	void RemoveCurve(){
		Undo.RecordObject(target, "Removed a curve");
		if(bezierPath.controlPoints.Count <= 4){
			bezierPath.controlPoints = null;
			return;
		}

		for(int i = 0; i < 3; i++){
			bezierPath.controlPoints.RemoveAt(bezierPath.controlPoints.Count-1);
		}
		EditorUtility.SetDirty(target);
	}


	CurveData GetCurveData(){
		float averageLength = 0;
		float minSpacing = 99999;
		float maxSpacing = 0;

		var drawingPoints = bezierPath.GetDrawingPoints();

		float[] distances = new float[drawingPoints.Count];

		for(int i = 0; i < drawingPoints.Count-1; i++){
			float distanceBetweenPoints = Vector3.Magnitude (drawingPoints[i] - drawingPoints[i+1]);
			minSpacing = (minSpacing < distanceBetweenPoints ? minSpacing : distanceBetweenPoints);
			maxSpacing = (maxSpacing > distanceBetweenPoints ? maxSpacing : distanceBetweenPoints);
			averageLength += distanceBetweenPoints;
			distances[i] = distanceBetweenPoints;
		}
		averageLength /= (float)drawingPoints.Count;
		return new CurveData(averageLength, maxSpacing, minSpacing, distances);
	}

	public float GetMaxDistanceBetweenPoints(){
		return 0;
	}

	private struct CurveData{
		public float averageLegth;
		public float maxSpacing;
		public float minSpacing;
		public float [] distances;

		public CurveData(float averageLegth, float maxSpacing, float minSpacing, float[] distances){
			this.averageLegth = averageLegth;
			this.maxSpacing = maxSpacing;
			this.minSpacing = minSpacing;
			this.distances = distances;
		}
	}

}
